home *** CD-ROM | disk | FTP | other *** search
/ Adobe Graphics & Publishing SDK 1996 December / Adobe Graphics & Publishing SDK 1996 December.iso / mac / Premiere 4.2 SDK r3 Mac / Examples / Projects / Burn-Time-Code / Burn-Time-Code.c < prev    next >
Text File  |  1996-01-25  |  21KB  |  651 lines

  1. //========================================================================================
  2. //
  3. // Burn Time Code.c - Burn Time Code on to a frame in Adobe Premiere¬.
  4. //
  5. // Written by Bryan K. "Beaker" Ressler.
  6. //
  7. // Copyright ⌐ 1993-96, Adobe Systems Incorporated, all rights reserved worldwide.
  8. //
  9. // Version    1.00    10/20/93    Original version.
  10. // Version    1.01    9/12/94        Updated for 4.0.
  11. // Version  1.02    10/6/95     Updated for 4.2 and CW7.
  12. //
  13. //========================================================================================
  14.  
  15. //========================================================================================
  16. // Includes - use precompiled headers if compiling with CodeWarrior.
  17. //========================================================================================
  18. #ifdef __MWERKS__
  19.     #ifdef powerc
  20.         #include "PremierePPC"
  21.     #else
  22.         #include "Premiere68k"
  23.     #endif
  24. #else
  25.     #include "Premiere.h"
  26. #endif
  27.  
  28. //========================================================================================
  29. // Resource IDs
  30. //========================================================================================
  31. #define kDialog                100        // DLOG - Our setings dialog
  32. #define kStrings            100        // STR# - Our string list
  33.  
  34. //========================================================================================
  35. // Strings in our string list
  36. //========================================================================================
  37. #define kBkgdPrompt            1        // Color picker prompt for background color
  38. #define kTextPrompt            2        // Color picker prompt for text color
  39.  
  40. //========================================================================================
  41. // Settings dialog items
  42. //========================================================================================
  43. enum {
  44.     riOK = 1,
  45.     riCancel,
  46.     riSample,
  47.     riStartTimeText,
  48.     riStartTimeInterp,        // 5
  49.     riDropFrameCheck,
  50.     riPositionPopup,
  51.     riBkgdSwatch,
  52.     riFontPopup,
  53.     riBoldCheck,            // 10
  54.     riItalicCheck,
  55.     riTextSwatch
  56. };
  57.  
  58. enum {                        // Time code positions
  59.     posLeft = 1,
  60.     posCenter,
  61.     posRight
  62. };
  63.  
  64. //========================================================================================
  65. // Private types
  66. //========================================================================================
  67. typedef struct {
  68.     short            version;        // BCD record version number (e.g. 0x0120 = 1.2)
  69.     long            startTime;        // Start time in frames
  70.     Str255            fontName;        // The font for the time code display
  71.     RGBColor        bkgdColor;        // The background color
  72.     RGBColor        textColor;        // The text color
  73.     short            position;        // Time code position (left/center/right)
  74.     Boolean            isBold;            // Bold text?
  75.     Boolean            isItalic;        // Italic text?
  76.     Boolean            dropFrame;        // Drop-frame time code?
  77. } BTCSpecRec, *BTCSpecPtr, **BTCSpecHdl;
  78.  
  79. typedef struct {
  80.     BTCSpecRec        btcSpec;
  81.     VideoHandle        theData;
  82. } LocalRec, **LocalHand;
  83.  
  84. //========================================================================================
  85. // Constants
  86. //========================================================================================
  87. #define kSpecsVersion            0x0120    // Version of our specs record (1.2)
  88.  
  89. //========================================================================================
  90. // Static prototypes
  91. //========================================================================================
  92. static void InitBTCSpec(BTCSpecPtr bs);
  93. static short BTCSetup(VideoHandle theData);
  94. static void SwapBTCColors(DialogPtr dialog, BTCSpecPtr bs);
  95. static Boolean OptionDown(void);
  96. static pascal Boolean BTCFilter(DialogPtr dialog, EventRecord *event, short *hit);
  97. static short FindMenuItem(MenuHandle menu, StringPtr name);
  98. static short SetupFontMenu(BTCSpecPtr bs, MenuHandle menu);
  99. static pascal void SwatchItem(DialogPtr dialog, short item);
  100. static pascal void SampleItem(DialogPtr dialog, short item);
  101. static void ImageProc(BTCSpecPtr bs, GWorldPtr src, GWorldPtr target, Rect *overlaySize,
  102.     short fps, long part, short sizeFlags);
  103. static PicHandle MakeOverlayPicture(BTCSpecPtr bs, short width, short height, short fps,
  104.     long part, short sizeFlags);
  105. static short SetupText(BTCSpecPtr bs, Rect *box);
  106. static void DrawTimeCode(BTCSpecPtr bs, Rect *box, short textSize, short fps, long part);
  107.  
  108. //========================================================================================
  109. // Dispatcher for filter functions.
  110. //========================================================================================
  111. pascal short main (short selector, VideoHandle theData)
  112. {
  113.     BTCSpecHdl    camHdl;
  114.     CGrafPtr    oldWorld;
  115.     GDHandle    oldDevice;
  116.     short        fps, result = 0;
  117.  
  118.     switch (selector) {
  119.         case fsExecute:
  120.             camHdl = (BTCSpecHdl)(*theData)->specsHandle;
  121.             if (camHdl != nil) {
  122.                 GetGWorld(&oldWorld, &oldDevice);
  123.                 SetGWorld((*theData)->destination, nil);
  124.                 HLock((Handle)camHdl);
  125.                 fps = (*theData)->fps;
  126.                 if ((*camHdl)->dropFrame)
  127.                     fps |= 0x8000;
  128.                 ImageProc(*camHdl, (*theData)->source, (*theData)->destination, nil, fps,
  129.                     (*theData)->part, (*theData)->sizeFlags);
  130.                 HUnlock((Handle)camHdl);
  131.                 ForeColor(blackColor);
  132.                 BackColor(whiteColor);
  133.                 SetGWorld(oldWorld, oldDevice);
  134.             } else result = 1;
  135.             break;
  136.             
  137.         case fsSetup:
  138.             result = BTCSetup(theData);
  139.             break;
  140.     }
  141.     return(result);
  142. }
  143.  
  144. //========================================================================================
  145. // Initialize a burn time code specification record with reasonable default values. If bs
  146. // is in a Handle, it should be locked before calling this routine, because we call
  147. // GetFontName, which moves memory.
  148. //========================================================================================
  149. static void InitBTCSpec(BTCSpecPtr bs)
  150. {
  151.     RGBColor    white = { 0xffff, 0xffff, 0xffff };
  152.     RGBColor    black = { 0x0000, 0x0000, 0x0000 };
  153.     
  154.     bs->version = kSpecsVersion;
  155.     bs->startTime = 0;
  156.     GetFontName(applFont, bs->fontName);
  157.     bs->bkgdColor = black;
  158.     bs->textColor = white;
  159.     bs->position = posCenter;
  160.     bs->isBold = false;
  161.     bs->isItalic = false;
  162.     bs->dropFrame = false;
  163. }
  164.  
  165. //========================================================================================
  166. // Conduct the Burn Time Code filter's setup dialog.
  167. //========================================================================================
  168. static short BTCSetup(VideoHandle theData)
  169. {
  170.     Str63        prompt, timeStr;
  171.     RGBColor    oldColor, newColor;
  172.     LocalHand    ldata;
  173.     MenuHandle    menu;
  174.     DialogPtr    dialog;
  175.     GrafPtr        oldPort;
  176.     BTCSpecPtr    bs;
  177.     long        temp;
  178.     short        item, result = 0;
  179.     short        origfps, fps;
  180.     unsigned char    blank = 0;
  181.     Boolean        redrawSample = false;
  182.  
  183.     InitCursor();
  184.     GetPort(&oldPort);
  185.     ldata = (LocalHand)NewHandleClear(sizeof(LocalRec));
  186.     if (ldata == nil)
  187.         return(1);
  188.     
  189.     // Get our spec if one was provided, otherwise initialize with defaults
  190.     HLock((Handle)ldata);
  191.     (*ldata)->theData = theData;
  192.     bs = &(*ldata)->btcSpec;
  193.     origfps = fps = (*theData)->fps;
  194.     if (bs->dropFrame)
  195.         fps |= 0x8000;
  196.     if ((*theData)->specsHandle) {
  197.         BlockMove(*(*theData)->specsHandle, (Ptr)bs, sizeof(BTCSpecRec));
  198.         if (bs->version != kSpecsVersion)
  199.             InitBTCSpec(bs);
  200.     } else InitBTCSpec(bs);
  201.     
  202.     // Get the dialog
  203.     dialog = GetNewDialog(kDialog, nil, (WindowPtr)-1);
  204.     SetPort(dialog);
  205.     CenterWindow(dialog, &(*GetMainDevice())->gdRect);
  206.     SetWRefCon(dialog,(long)ldata);
  207.  
  208.     // Set up the user items
  209.     UserItem(dialog, riSample, SampleItem);
  210.     UserItem(dialog, riBkgdSwatch, SwatchItem);
  211.     UserItem(dialog, riTextSwatch, SwatchItem);
  212.     
  213.     // Set control values
  214.     SetCValue(dialog, riBoldCheck, bs->isBold);
  215.     SetCValue(dialog, riItalicCheck, bs->isItalic);
  216.     SetCValue(dialog, riPositionPopup, bs->position);
  217.     SetCValue(dialog, riDropFrameCheck, bs->dropFrame);
  218.     
  219.     // Set edit item text
  220.     Time2Str(bs->startTime, timeStr, fps, tsHours);
  221.     SetEText(dialog, riStartTimeText, timeStr);
  222.     ParamText(timeStr, &blank, &blank, &blank);
  223.     SelIText(dialog, riStartTimeText, 0, 32767);
  224.     
  225.     // Set up the font menu
  226.     menu = (MenuHandle)GetCRef(dialog, riFontPopup);
  227.     item = SetupFontMenu(bs, menu);
  228.     SetCValue(dialog, riFontPopup, item);
  229.     WidenMenu(dialog, riFontPopup);
  230.     
  231.     // Pose the dialog
  232.     ShowModal(dialog);
  233.     do {
  234.         item = 0;
  235.         if (redrawSample) {
  236.             redrawSample = false;
  237.             SampleItem(dialog, riSample);
  238.         }
  239.         PrModalDialog(BTCFilter, &item);
  240.         switch (item) {
  241.             case riOK:
  242.                 // Store our camcorder filter specification
  243.                 if ((*theData)->specsHandle) {
  244.                     SafeSetHandleSize((*theData)->specsHandle, sizeof(BTCSpecRec));
  245.                     BlockMove((Ptr)bs, *(*theData)->specsHandle,
  246.                         sizeof(BTCSpecRec));
  247.                 } else {
  248.                     (*theData)->specsHandle = NewHandle(sizeof(BTCSpecRec));
  249.                     if ((*theData)->specsHandle)
  250.                         BlockMove((Ptr)bs, *(*theData)->specsHandle,
  251.                             sizeof(BTCSpecRec));
  252.                 }
  253.                 break;
  254.             case riCancel:
  255.                 item = riOK;
  256.                 result = 1;
  257.                 break;
  258.             case riStartTimeText:
  259.                 GetEText(dialog, item, timeStr);
  260.                 fps = origfps;
  261.                 if (bs->dropFrame)
  262.                     fps |= 0x8000;
  263.                 bs->startTime = Str2Time(timeStr + 1, *timeStr, fps);
  264.                 Time2Str(bs->startTime, timeStr, fps, tsHours);
  265.                 ParamText(timeStr, &blank, &blank, &blank);
  266.                 InvalItem(dialog, riStartTimeInterp);
  267.                 redrawSample = true;
  268.                 break;
  269.             case riDropFrameCheck:
  270.                 bs->dropFrame = !bs->dropFrame;
  271.                 SetCValue(dialog, item, bs->dropFrame);
  272.                 redrawSample = true;
  273.                 break;
  274.             case riPositionPopup:
  275.                 bs->position = GetCValue(dialog, item);
  276.                 redrawSample = true;
  277.                 break;
  278.             case riBkgdSwatch:
  279.                 if (OptionDown())
  280.                     SwapBTCColors(dialog, bs);
  281.                 else {
  282.                     GetIndString(prompt, kStrings, kBkgdPrompt);
  283.                     oldColor = bs->bkgdColor;
  284.                     if (GeneralGetColor(prompt, &oldColor, &newColor)) {
  285.                         bs->bkgdColor = newColor;
  286.                         SwatchItem(dialog, item);
  287.                     }
  288.                     SetPort(dialog);
  289.                     ParamText(timeStr, &blank, &blank, &blank);
  290.                 }
  291.                 redrawSample = true;
  292.                 break;
  293.             case riFontPopup:
  294.                 temp = GetCValue(dialog, item);
  295.                 menu = (MenuHandle)GetCRef(dialog, riFontPopup);
  296.                 GetItem(menu, temp, bs->fontName);
  297.                 redrawSample = true;
  298.                 break;
  299.             case riBoldCheck:
  300.                 bs->isBold = !bs->isBold;
  301.                 SetCValue(dialog, item, bs->isBold);
  302.                 redrawSample = true;
  303.                 break;
  304.             case riItalicCheck:
  305.                 bs->isItalic = !bs->isItalic;
  306.                 SetCValue(dialog, item, bs->isItalic);
  307.                 redrawSample = true;
  308.                 break;
  309.             case riTextSwatch:
  310.                 if (OptionDown())
  311.                     SwapBTCColors(dialog, bs);
  312.                 else {
  313.                     GetIndString(prompt, kStrings, kTextPrompt);
  314.                     oldColor = bs->textColor;
  315.                     if (GeneralGetColor(prompt, &oldColor, &newColor)) {
  316.                         bs->textColor = newColor;
  317.                         SwatchItem(dialog, item);
  318.                     }
  319.                     SetPort(dialog);
  320.                     ParamText(timeStr, &blank, &blank, &blank);
  321.                 }
  322.                 redrawSample = true;
  323.                 break;
  324.             default:
  325.                 break;
  326.         }
  327.     } while (item != riOK);
  328.     
  329.     // Clean up
  330.     HUnlock((Handle)ldata);
  331.     DisposeModal(dialog);
  332.     DisposeHandle((Handle)ldata);
  333.     SetPort(oldPort);
  334.     return(result);
  335. }
  336.  
  337. //========================================================================================
  338. // Swaps the background and text colors
  339. //========================================================================================
  340. static void SwapBTCColors(DialogPtr dialog, BTCSpecPtr bs)
  341. {
  342.     RGBColor    temp;
  343.     
  344.     temp = bs->bkgdColor;
  345.     bs->bkgdColor = bs->textColor;
  346.     bs->textColor = temp;
  347.     SwatchItem(dialog, riBkgdSwatch);
  348.     SwatchItem(dialog, riTextSwatch);
  349. }
  350.  
  351. //========================================================================================
  352. // Returns true if the Option key is currently down.
  353. //========================================================================================
  354. static Boolean OptionDown(void)
  355. {
  356.     return((GetModifiers(nil) & bOption) != 0);
  357. }
  358.  
  359. //========================================================================================
  360. // Dialog filter for the Burn Time Code setup dialog. It does the hits on popup control
  361. // items.
  362. //========================================================================================
  363. static pascal Boolean BTCFilter(DialogPtr dialog, EventRecord *event, short *hit)
  364. {
  365.     Point            where;
  366.     ControlHandle    thecontrol;
  367.     short            which, oldval, part;
  368.     Boolean            result = false;
  369.     
  370.     result = modalfilter(dialog, event, hit);
  371.     if (!result && event->what == mouseDown) {
  372.         where = event->where;
  373.         GlobalToLocal(&where);
  374.         if (which = FindDItem(dialog, where) + 1) {
  375.             if (FindControl(where, dialog, &thecontrol)) {
  376.                 oldval = GetCtlValue(thecontrol);
  377.                 part = TrackControl(thecontrol, where, nil);
  378.                 if (oldval != GetCtlValue(thecontrol)) {
  379.                     *hit = which;
  380.                     result = true;
  381.                 } else if (part) {
  382.                     *hit = which;
  383.                     result = true;
  384.                 } else {
  385.                     *hit = 0;
  386.                     result = true;
  387.                 }
  388.             }
  389.         }
  390.     }
  391.     
  392.     return(result);
  393. }
  394.  
  395. //========================================================================================
  396. // Given a menu and an item name, return the item number or 0 if not found.
  397. //========================================================================================
  398. static short FindMenuItem(MenuHandle menu, StringPtr name)
  399. {
  400.     short    numItems, item = 0, i = 0;
  401.     Str255    str;
  402.  
  403.     numItems = CountMItems(menu);
  404.     for (i = 1; i <= numItems; i++) {
  405.         GetItem(menu, i, str);
  406.         if (StringsSame(str, name))
  407.             item = i;
  408.     }
  409.     return item;
  410. }
  411.  
  412. //========================================================================================
  413. // Deletes any existing item from the font menu, appends all installed font names, then
  414. // tries to find the current font in the menu, returning its item number if found or 0 if
  415. // not found.
  416. //========================================================================================
  417. static short SetupFontMenu(BTCSpecPtr bs, MenuHandle menu)
  418. {
  419.     short    i;
  420.     
  421.     // First, delete all existing items from the menu
  422.     i = CountMItems(menu);
  423.     do DelMenuItem(menu, i--); while (i > 0);
  424.  
  425.     // Add the fonts
  426.     AddResMenu(menu, 'FONT');
  427.     
  428.     // Look for the current font in the menu
  429.     i = FindMenuItem(menu, bs->fontName);
  430.     return(i);
  431. }
  432.  
  433. //========================================================================================
  434. // Draw the color swatches (both handled by this user item proc).
  435. //========================================================================================
  436. static pascal void SwatchItem(DialogPtr dialog, short item)
  437. {
  438.     Rect        box;
  439.     RGBColor    color;
  440.     LocalHand    ldata;
  441.  
  442.     ldata = (LocalHand)GetWRefCon(dialog);
  443.     switch (item) {
  444.         case riBkgdSwatch:
  445.             color = (*ldata)->btcSpec.bkgdColor;
  446.             break;
  447.         case riTextSwatch:
  448.             color = (*ldata)->btcSpec.textColor;
  449.             break;
  450.     }
  451.     GetDRect(dialog, item, &box);
  452.     FrameRect(&box);
  453.     InsetRect(&box, 1, 1);
  454.     DitherBox(dialog, &box, &color);
  455.     SetGray(0);
  456. }
  457.  
  458. //========================================================================================
  459. // Draw the sample in the setup dialog.
  460. //========================================================================================
  461. static pascal void SampleItem(DialogPtr dialog, short item)
  462. {
  463.     Rect        all, box;
  464.     LocalHand    ldata;
  465.     VideoHandle    theData;
  466.     GWorldPtr    src, dest;
  467.     Rect        overlaySize = { 0, 0, 480, 640 };    // Render at 640 x 480 for preview
  468.     short        fps;
  469.  
  470.     ldata = (LocalHand)GetWRefCon(dialog);
  471.     theData = (*ldata)->theData;
  472.         
  473.     src = (*theData)->source;
  474.     dest = (*theData)->destination;
  475.     box = src->portRect;
  476.  
  477.     GetDRect(dialog, item, &all);
  478.     FrameRect(&all);
  479.     InsetRect(&all, 1, 1);
  480.  
  481.     // ldata is already locked here (by the dialog routine)
  482.     fps = (*theData)->fps;
  483.     if ((*ldata)->btcSpec.dropFrame)
  484.         fps |= 0x8000;
  485.     ImageProc(&(*ldata)->btcSpec, src, dest, &overlaySize, fps , 0, (*theData)->sizeFlags);
  486.     CopyBits((BitMap *)&dest->portPixMap, &dialog->portBits, &box, &all, ditherCopy,
  487.         dialog->visRgn);
  488. }
  489.  
  490. //========================================================================================
  491. // Apply our filter between the src and dest worlds. The overlaySize rectangle allows the
  492. // caller to force the picture to be rendered at a given size. This allows the SampleItem
  493. // proc to image the overlay picture at the output options size, so the scaled down over-
  494. // lay will be more accurate. Note that the image is still overlayed at the size
  495. // specified. FPS SHOULD ALREADY HAVE THE HIGH BIT SET IF DROP FRAME IS TURNED ON!
  496. //========================================================================================
  497. static void ImageProc(BTCSpecPtr bs, GWorldPtr src, GWorldPtr target, Rect *overlaySize,
  498.     short fps, long part, short sizeFlags)
  499. {
  500.     Rect        box;
  501.     GDHandle    oldGD;
  502.     PicHandle    overlay;
  503.     GWorldPtr    oldGW;
  504.     short        hSize, vSize;
  505.     
  506.     // Point at target
  507.     GetGWorld(&oldGW, &oldGD);
  508.     SetGWorld(target, nil);
  509.     
  510.     // Copy the frame into the target
  511.     box = src->portRect;
  512.     CopyBits((BitMap*)&src->portPixMap, (BitMap*)&target->portPixMap, &box, &box,
  513.         srcCopy, nil);
  514.     
  515.     // Create the camcorder overlay picture and draw it over the destination port.
  516.     if (overlaySize != nil) {
  517.         hSize = overlaySize->right - overlaySize->left;
  518.         vSize = overlaySize->bottom - overlaySize->top;
  519.     } else {
  520.         hSize = box.right - box.left;
  521.         vSize = box.bottom - box.top;
  522.     }
  523.     overlay = MakeOverlayPicture(bs, hSize, vSize, fps, part, sizeFlags);
  524.     if (overlay)
  525.         DrawPicture(overlay, &box);
  526.     KillPicture(overlay);
  527.     
  528.     // Restore
  529.     ForeColor(blackColor); PenNormal();
  530.     SetGWorld(oldGW, oldGD);
  531. }
  532.  
  533. //========================================================================================
  534. // The supervisor routine that constructs the timecode overlay picture. Returns nil if
  535. // there wasn't enough memory to build the picture.
  536. //========================================================================================
  537. static PicHandle MakeOverlayPicture(BTCSpecPtr bs, short width, short height, short fps,
  538.     long part, short sizeFlags)
  539. {
  540.     Rect        box;
  541.     short        textSize;
  542.     PicHandle    pict;
  543.  
  544.     // Build the picture bounds and compensate for fields, half-vertical, and half-horiz-
  545.     // ontal flags.
  546.     SetRect(&box, 0, 0, width, height);
  547.     if (sizeFlags & gvHalfH)
  548.         box.right *= 2;
  549.     if (sizeFlags & (gvHalfV + gvFieldsEven + gvFieldsOdd))
  550.         box.bottom *= 2;
  551.     if (sizeFlags & (gvFieldsEven + gvFieldsOdd))
  552.         part /= 2;
  553.  
  554.     // Build the overlay picture
  555.     pict = OpenPicture(&box);
  556.     
  557.         // Start clean
  558.         ForeColor(blackColor);
  559.         PenNormal();
  560.         
  561.         // Set up text parameters
  562.         textSize = SetupText(bs, &box);
  563.     
  564.         // Burn the timecode
  565.         DrawTimeCode(bs, &box, textSize, fps, part);
  566.         
  567.         // End clean
  568.         ForeColor(blackColor);
  569.         PenNormal();
  570.     
  571.     ClosePicture();
  572.     return(pict);
  573. }
  574.  
  575. //========================================================================================
  576. // Set up the current port with the appropriate font, size, style, and mode.
  577. //========================================================================================
  578. static short SetupText(BTCSpecPtr bs, Rect *box)
  579. {
  580.     short    fNum, size;
  581.     Style    style;
  582.  
  583.     // Get the information    
  584.     GetFNum(bs->fontName, &fNum);
  585.     size = (box->right - box->left) / 20;
  586.     if (size < 12) size = 12;
  587.     style = normal;
  588.     if (bs->isBold)
  589.         style |= bold;
  590.     if (bs->isItalic)
  591.         style |= italic;
  592.     
  593.     // Apply it
  594.     TextFont(fNum);
  595.     TextFace(style);
  596.     TextMode(srcOr);
  597.     TextSize(size);
  598.     
  599.     return(size);
  600. }
  601.  
  602. //========================================================================================
  603. // Draw the "burned-in" timecode.
  604. //========================================================================================
  605. static void DrawTimeCode(BTCSpecPtr bs, Rect *box, short textSize, short fps, long part)
  606. {
  607.     Str63        timeStr;
  608.     FontInfo    info;
  609.     Rect        tcBox;
  610.     RGBColor    fiftyPercentGray = { 0x7fff, 0x7fff, 0x7fff };
  611.     short        width, height, tcWidth, tcGutter;
  612.     
  613.     // Build the timecode string and get its width
  614.     Time2Str(bs->startTime + part, timeStr, fps, tsHours);
  615.     tcWidth = StringWidth(timeStr);
  616.     tcGutter = tcWidth * 4 / 100;                                // 4% gutters on each side
  617.  
  618.     // Build the box in which the timecode string will be drawn
  619.     tcBox = *box;
  620.     width = tcBox.right - tcBox.left;
  621.     height = tcBox.bottom - tcBox.top;
  622.     InsetRect(&tcBox, width * 12 / 100, height * 12 / 100);        // 12% - title-safe
  623.     tcBox.top = tcBox.bottom - (textSize * 120 / 100);            // 120% of the point size
  624.     switch (bs->position) {
  625.         case posLeft:
  626.             tcBox.right = tcBox.left + tcGutter + tcWidth + tcGutter;
  627.             break;
  628.         case posCenter:
  629.             tcWidth = tcWidth + tcGutter * 2;
  630.             tcBox.left += ((tcBox.right - tcBox.left) - tcWidth) / 2;
  631.             tcBox.right = tcBox.left + tcWidth;
  632.             break;
  633.         case posRight:
  634.             tcBox.left = tcBox.right - tcGutter - tcWidth - tcGutter;
  635.             break;
  636.     }
  637.     
  638.     // Draw the background
  639.     OpColor(&fiftyPercentGray);
  640.     RGBForeColor(&bs->bkgdColor);
  641.     PenMode(blend);
  642.     PaintRect(&tcBox);
  643.     
  644.     // Draw the timecode
  645.     PenNormal();
  646.     GetFontInfo(&info);
  647.     RGBForeColor(&bs->textColor);
  648.     MoveTo(tcBox.left + tcGutter, tcBox.bottom - info.descent);
  649.     DrawString(timeStr);
  650. }
  651.